package com.ego.product.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ego.common.constant.SystemConstant;
import com.ego.common.exception.GlobalException;
import com.ego.common.to.SkuEsTo;
import com.ego.common.util.PageUtil;
import com.ego.common.util.Result;
import com.ego.common.vo.PageVo;
import com.ego.common.vo.product.SkuInStockVo;
import com.ego.product.feign.ThirdPartyFeign;
import com.ego.product.feign.WareFeign;
import com.ego.product.po.*;
import com.ego.product.service.*;
import com.ego.product.to.coupon.SkuReductionTo;
import com.ego.product.vo.SpuInfoSearchVo;
import com.ego.product.vo.spu.BaseAttr;
import com.ego.product.vo.spu.Image;
import com.ego.product.vo.spu.Sku;
import com.ego.product.vo.spu.SpuSaveVo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.ego.product.dao.SpuInfoDao;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * @author KamTang
 * @program: ego
 * @description spu信息对应实现类
 * @date 2022-11-13 12:56:14
 */
@Service("spuInfoService")
public class SpuInfoServiceImpl extends ServiceImpl<SpuInfoDao, SpuInfo> implements SpuInfoService {

    @Autowired
    private SpuInfoDescService spuInfoDescService;
    @Autowired
    private SpuImageService spuImageService;
    @Autowired
    private AttrService attrService;
    @Autowired
    private ProductAttrValueService productAttrValueService;
    @Autowired
    private SkuInfoService skuInfoService;
    @Autowired
    private SkuImageService skuImageService;
    @Autowired
    private SkuSaleAttrValueService skuSaleAttrValueService;
    @Autowired
    private WareFeign wareFeign;
    @Autowired
    private BrandService brandService;
    @Autowired
    private CategoryService categoryService;
    @Autowired
    private ThirdPartyFeign thirdPartyFeign;
    @Autowired
    private ThreadPoolExecutor executor;

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void saveSpu(SpuSaveVo vo) throws ExecutionException, InterruptedException {
        // 保存商品基本信息：pms_spu_info
        SpuInfo spuInfo = SpuInfo.fromVo(vo).setCreateTime(new Date()).setUpdateTime(new Date());
        this.save(spuInfo);

        CompletableFuture<Void> spuInfoDescTask = CompletableFuture.runAsync(() -> {
            // 保存商品描述：pms_spu_info_desc
            List<String> descriptions = vo.getDescription();
            SpuInfoDesc descEntity = new SpuInfoDesc()
                    .setSpuId(spuInfo.getId()).setDescription(String.join(",", descriptions));
            spuInfoDescService.save(descEntity);
        }, executor);

        CompletableFuture<Void> spuImageTask = CompletableFuture.runAsync(() -> {
            // 保存图片：pms_spu_image
            List<String> images = vo.getImages();
            spuImageService.saves(spuInfo.getId(), images);
        }, executor);

        CompletableFuture<Void> productAttrValueTask = CompletableFuture.runAsync(() -> {
            List<BaseAttr> baseAttrs = vo.getBaseAttrs();
            List<ProductAttrValue> productAttrValues = baseAttrs.stream().map(baseAttr -> {
                Attr attr = attrService.getById(baseAttr.getAttrId());
                return ProductAttrValue.build(baseAttr, attr, spuInfo);
            }).collect(Collectors.toList());
            productAttrValueService.saveBatch(productAttrValues);
        }, executor);


        // 调用远程服务coupon保存积分信息：egomall_sms.sms_spu_bounds
        //if (ObjectUtils.isNotEmpty(vo.getBound())) {
        //    BoundTo boundTo = vo.getBound().toTo();
        //    boundTo.setSpuId(spuInfo.getId());
        //    Result r1 = couponFeign.saveSpuBound(boundTo);
        //    if (r1.getCode() != SystemConstant.Status.SUCCESS.getCode()) {
        //        log.error("远程保存spu积分信息失败");
        //        throw new GlobalException("couponFeign.saveSpuBound()调用失败", 500);
        //    }
        //}

        CompletableFuture<Void> otherTask = CompletableFuture.runAsync(() -> {
            List<Sku> skus = vo.getSkus();
            if (!CollectionUtils.isEmpty(skus)) {
                skus.forEach(sku -> {
                    String defaultImg = "";
                    for (Image image : sku.getImages()) {
                        if (image.getDefaultImg() == 1) {
                            defaultImg = image.getImgUrl();
                        }
                    }
                    // 保存当前spu商品信息对应的sku信息：pms_sku_info
                    SkuInfo skuInfo = SkuInfo.build(spuInfo, sku, defaultImg);
                    skuInfoService.save(skuInfo);

                    // 过滤并保存sku图片：pms_sku_image
                    List<SkuImage> skuImages = sku.getImages().stream()
                            .map(image -> SkuImage.build(skuInfo.getSkuId(), image))
                            .filter(e -> StringUtils.isNotEmpty(e.getImgUrl())).collect(Collectors.toList());
                    skuImageService.saveBatch(skuImages);

                    // 保存sku销售属性名以及对应的销售属性值：pms_sku_sale_attr_value
                    List<SkuSaleAttrValue> skuSaleAttrValues = sku.getAttr().stream()
                            .map(attr -> SkuSaleAttrValue.build(attr, skuInfo.getSkuId())).collect(Collectors.toList());
                    skuSaleAttrValueService.saveBatch(skuSaleAttrValues);

                    // 调用远程优惠服务端保存sku优惠、满减等信息：egomall_sms.sms_sku_ladder\sms_sku_full_reduction\sms_member_price
                    SkuReductionTo skuReductionTo = sku.buildSkuReductionTo(skuInfo);
                    // 如果不设置sku优惠、满减等信息，不走远程调用
                    if (skuReductionTo.getFullCount() > 0 || skuReductionTo.getFullPrice().compareTo(BigDecimal.ZERO) > 0) {
                        log.error("远程保存sku优惠信息失败");
                        throw new GlobalException(SystemConstant.Status.COUPON_FEIGN_EXCEPTION.getMsg(),
                                SystemConstant.Status.COUPON_FEIGN_EXCEPTION.getCode());
                    }
                });
            }
        }, executor);
        CompletableFuture.allOf(spuImageTask, spuInfoDescTask, productAttrValueTask, otherTask).get();
    }

    @Override
    public PageVo<SpuInfo> search(SpuInfoSearchVo search) {
        // 使用and避免SQL执行时跳过eq，直接走like。注意：and使用之前先判断关键字是否存在，不然编译后的SQL括号不对齐。
        // 比如：SELECT * FROM pms_spu_info WHERE (publish_status = ? AND id LIKE ? OR spu_name LIKE)
        // 当满足spu_name时，就没法将publish_status条件构造。
        LambdaQueryWrapper<SpuInfo> query = Wrappers.<SpuInfo>lambdaQuery()
                .eq(search.getStatus() != null, SpuInfo::getPublishStatus, search.getStatus())
                .eq(search.getBrandId() != null && search.getBrandId() != 0, SpuInfo::getBrandId, search.getBrandId())
                .eq(search.getCategoryId() != null && search.getCategoryId() != 0, SpuInfo::getCategoryId, search.getCategoryId());
        if (StringUtils.isNotEmpty(search.getKeyword())) {
            query.and(q -> q.like(SpuInfo::getId, search.getKeyword())
                    .or().like(SpuInfo::getSpuName, search.getKeyword())
                    .or().like(SpuInfo::getSpuDescription, search.getKeyword()));
        }

        Page<SpuInfo> page = this.baseMapper.selectPage(Page.of(search.getPageNum(), search.getPageSize()), query);
        return PageUtil.toPage(page);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void sale(Long spuId) {
        // 查出当前spuId对应的所有sku信息，品牌名字
        List<SkuInfo> skuInfos = skuInfoService.getBySpuId(spuId);
        List<Long> skuIds = skuInfos.stream().map(SkuInfo::getSkuId).collect(Collectors.toList());

        // 查询当前sku的所有可以被用来检索的规格属性
        List<ProductAttrValue> productAttrValues = productAttrValueService.get(spuId);
        List<Long> attrIds = productAttrValues.stream().map(ProductAttrValue::getAttrId).collect(Collectors.toList());

        List<Long> retrievableIds = attrService.getRetrievableId(attrIds);
        Set<Long> retrievableIdSet = new HashSet<>(retrievableIds);

        List<SkuEsTo.Attrs> attrs = productAttrValues.stream()
                .filter(productAttrValue -> retrievableIdSet.contains(productAttrValue.getAttrId()))
                .map(ProductAttrValue::fill)
                .collect(Collectors.toList());

        // 发送远程调用，库存系统查询是否有库存
        Map<Long, Boolean> stockMap = null;
        try {
            Result result = wareFeign.inStock(skuIds);
            TypeReference<List<SkuInStockVo>> typeReference = new TypeReference<List<SkuInStockVo>>() {
            };
            stockMap = result.getData(typeReference).stream()
                    .collect(Collectors.toMap(SkuInStockVo::getSkuId, SkuInStockVo::getHasStock));
        } catch (Exception e) {
            log.error("库存服务查询异常：原因{}", e);
        }

        // 封装每个sku信息
        Map<Long, Boolean> finalStockMap = stockMap;
        List<SkuEsTo> skuEsTos = skuInfos.stream().map(skuInfo -> {
            Brand brand = brandService.getById(skuInfo.getBrandId());
            Category category = categoryService.getById(skuInfo.getCategoryId());
            return skuInfo.toTo(finalStockMap, brand, category, attrs);
        }).collect(Collectors.toList());


        // 将数据发送至ES服务进行保存
        try {
            Result result = thirdPartyFeign.put(skuEsTos);
            if (result.getCode() == SystemConstant.Status.SUCCESS.getCode()) {
                SpuInfo spuInfo = new SpuInfo();
                spuInfo.onSale(spuId);
                this.baseMapper.updateById(spuInfo);
            } else {
                // TODO 7、重复调用？接口幂等性；重试机制
                // Feign调用流程
            }
        } catch (Exception e) {
            log.error("检索服务查询异常：原因{}", e);
        }
    }

    @Override
    public SpuInfo get(Long skuId) {
        SkuInfo skuInfo = skuInfoService.getById(skuId);
        return this.getById(skuInfo.getSpuId());
    }

}
